/*
 * $RCSfile: TableUtil.java,v $
 * $Revision: 1.1 $
 * $Date: 2009-03-01 $
 *
 * Copyright (C) 2008 Skin, Inc. All rights reserved.
 *
 * This software is the proprietary information of Skin, Inc.
 * Use is subject to license terms.
 */
package com.skin.generate.gui.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import com.skin.generate.TemplateParser;
import com.skin.webcat.database.Column;
import com.skin.webcat.database.Database;
import com.skin.webcat.database.Table;
import com.skin.webcat.database.dialect.Dialect;
import com.skin.webcat.datasource.ConnectionManager;
import com.skin.webcat.util.ClassUtil;
import com.skin.webcat.util.Jdbc;

/**
 * <p>Title: TableUtil</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2006</p>
 * @author xuesong.net
 * @version 1.0
 */
public class TableUtil {
    private static final Logger logger = LoggerFactory.getLogger(TableUtil.class);

    /**
     * default
     */
    private TableUtil() {
    }

    /**
     * @param connectionName
     * @param tableName
     * @return Table
     * @throws Exception
     */
    public static Table getTable(String connectionName, String tableName) throws Exception {
        Connection connection = null;
        
        try {
            connection = ConnectionManager.getConnection(connectionName);
            Dialect dialect = Database.getDialect(connection);
            return dialect.getTable(null, null, tableName);
        }
        finally {
            Jdbc.close(connection);
        }
    }

    /**
     * @param connectionName
     * @param detail
     * @return List<Table>
     * @throws Exception
     */
    public static List<Table> getTableList(String connectionName, boolean detail) throws Exception {
        Connection connection = null;

        try {
            connection = ConnectionManager.getConnection(connectionName);
            Dialect dialect = Database.getDialect(connection);
            return dialect.getTableList(null, null, "%", new String[]{"TABLE", "VIEW"}, detail);
        }
        finally {
            Jdbc.close(connection);
        }
    }

    /**
     * @param table
     * @return Object[][]
     */
    public static Object[][] getColumns(Table table) {
        int i = 0;
        Object[][] data = new Object[table.getColumnCount()][];
        Iterator<Column> iterator = table.getColumns().iterator();

        while(iterator.hasNext()) {
            data[i] = new Object[9];
            Column column = iterator.next();
            data[i][0] = Boolean.TRUE;
            data[i][1] = column.getColumnName();
            data[i][2] = (column.getVariableName() != null ? column.getVariableName() : column.getColumnName());
            data[i][3] = column.getTypeName();
            data[i][4] = column.getJavaTypeName();
            data[i][5] = Integer.valueOf(column.getColumnSize());
            data[i][6] = Integer.valueOf(column.getDecimalDigits());
            data[i][7] = Integer.valueOf(column.getPrecision());
            data[i][8] = column.getNullable();
            i++;
        }
        return data;
    }
    
    /**
     * @param table
     */
    public static void setWrapper(Table table) {
        if(table != null && table.getColumns() != null) {
            for(Column column : table.getColumns()) {
                String javaTypeName = column.getJavaTypeName();

                if(javaTypeName == null) {
                    column.setJavaTypeName("String");
                }
                else if(javaTypeName.equals("boolean")) {
                    column.setJavaTypeName("Boolean");
                }
                else if(javaTypeName.equals("byte")) {
                    column.setJavaTypeName("Byte");
                }
                else if(javaTypeName.equals("short")) {
                    column.setJavaTypeName("Short");
                }
                else if(javaTypeName.equals("int")) {
                    column.setJavaTypeName("Integer");
                }
                else if(javaTypeName.equals("float")) {
                    column.setJavaTypeName("Float");
                }
                else if(javaTypeName.equals("double")) {
                    column.setJavaTypeName("Double");
                }
                else if(javaTypeName.equals("long")) {
                    column.setJavaTypeName("Long");
                }
                else if(javaTypeName.equals("char")) {
                    column.setJavaTypeName("Character");
                }
            }
        }
    }

    /**
     * @param file
     * @param charset
     * @return Table
     * @throws Exception
     */
    public static Table parse(File file, String charset) throws Exception {
        Reader reader = null;

        try {
            reader = getFileReader(file, charset);
            return parse(reader);
        }
        finally {
            if(reader != null) {
                try {
                    reader.close();
                }
                catch(IOException e) {
                }
            }
        }
    }

    /**
     * @param reader
     * @return Table
     * @throws Exception
     */
    public static Table parse(Reader reader) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse(new InputSource(reader));
        Element element = document.getDocumentElement();
        return TemplateParser.parseTable(element);
    }

    /**
     * @param file
     * @param charset
     * @return Reader
     * @throws IOException 
     */
    private static Reader getFileReader(File file, String charset) throws Exception {
        Exception exception = null;
        InputStream inputStream = null;

        try {
            inputStream = new FileInputStream(file);
            return new InputStreamReader(inputStream, charset);
        }
        catch(Exception e) {
            exception = e;
        }

        if(inputStream != null) {
            try {
                inputStream.close();
            }
            catch (IOException e) {
            }
        }
        throw exception;
    }

    /**
     * @param className
     * @return Table
     */
    public static Table from(String className) {
        Table table = new Table();

        try {
            Class<?> type = ClassUtil.getClass(className);
            String tableName = type.getSimpleName();

            if(tableName.endsWith("Model")) {
                tableName = tableName.substring(0, tableName.length() - 5);
            }

            tableName = tableName.toUpperCase();
            table.setAlias(tableName);
            table.setTableName(tableName);
            table.setTableType("TABLE");
            table.setQueryName(tableName);
            table.setClassName(type.getSimpleName());

            if(table.getClassName().endsWith("Model")) {
                String s = table.getClassName();
                s = s.substring(0, s.length() - 5);
                table.setClassName(s);
            }

            String methodName = null;
            Method[] methods = type.getMethods();

            for(int i = 0; i < methods.length; i++) {
                methodName = methods[i].getName();
                Class<?>[] parameterTypes = methods[i].getParameterTypes();

                if(methodName.startsWith("set") && parameterTypes.length == 1) {
                    String name = methodName.substring(3);
                    String columnName = name.toUpperCase();
                    Column column = new Column();
                    column.setColumnName(columnName);
                    column.setJavaTypeName(parameterTypes[0].getName());
                    column.setVariableName(Character.toLowerCase(name.charAt(0)) + name.substring(1));
                    column.setMethodGetter("get" + name);
                    column.setMethodSetter("set" + name);
                    table.add(column);
                }
            }
        }
        catch(Exception e) {
            logger.error(e.getMessage(), e);
        }
        return table;
    }

    /**
     * @param column
     * @return String
     */
    public static String toXml(Column column) {
        StringBuilder buffer = new StringBuilder();
        buffer.append("<column");
        append(buffer, "alias", column.getAlias());
        append(buffer, "columnName", column.getColumnName());
        append(buffer, "dataType", column.getDataType());
        append(buffer, "typeName", column.getTypeName());
        append(buffer, "columnSize", column.getColumnSize());
        append(buffer, "decimalDigits", column.getDecimalDigits());
        append(buffer, "nullable", column.getNullable());
        append(buffer, "precision", column.getPrecision());
        append(buffer, "variableName", column.getVariableName());
        append(buffer, "javaTypeName", column.getJavaTypeName());
        append(buffer, "methodSetter", column.getMethodSetter());
        append(buffer, "methodGetter", column.getMethodGetter());
        append(buffer, "remarks", column.getRemarks());
        buffer.append("/>").append("\r\n");
        return buffer.toString();
    }

    /**
     * @param table
     * @return String
     */
    public static String toXml(Table table) {
        StringBuilder buffer = new StringBuilder();
        buffer.append("<table");
        append(buffer, "alias", table.getAlias());
        append(buffer, "tableName", table.getTableName());
        append(buffer, "tableType", table.getTableType());
        append(buffer, "queryName", table.getQueryName());
        append(buffer, "className", table.getClassName());
        append(buffer, "remarks", table.getRemarks());
        buffer.append(">\r\n");

        Iterator<Column> iterator = table.getColumns().iterator();

        while(iterator.hasNext()) {
            Column column = iterator.next();
            buffer.append("    ");
            buffer.append(toXml(column));
        }
        buffer.append("</table>\r\n");
        return buffer.toString();
    }
    
    /**
     * @param buffer
     * @param name
     * @param value
     */
    private static void append(StringBuilder buffer, String name, Object value) {
        if(value != null) {
            buffer.append(" ");
            buffer.append(name);
            buffer.append("=\"");
            html(buffer, value.toString(), "\r\n");
            buffer.append("\"");
        }
    }

    /**
     * @param buffer
     * @param source
     * @param crlf
     */
    private static void html(StringBuilder buffer, String source, String crlf) {
        if(source == null) {
            return;
        }

        char c;
        for(int i = 0, size = source.length(); i < size; i++) {
            c = source.charAt(i);

            switch (c) {
                case '&': {
                    buffer.append("&amp;");
                    break;
                }
                case '"': {
                    buffer.append("&quot;");
                    break;
                }
                case '<': {
                    buffer.append("&lt;");
                    break;
                }
                case '>': {
                    buffer.append("&gt;");
                    break;
                }
                case '\'':
                    buffer.append("&#39;");
                    break;
                case '\r': {
                    if((i + 1) < size) {
                        if(source.charAt(i + 1) == '\n') {
                            buffer.append(crlf);
                            i++;
                        }
                        else {
                            buffer.append(c);
                        }
                    }
                    else {
                        buffer.append(c);
                    }
                    break;
                }
                case '\n': {
                    buffer.append(crlf);
                    break;
                }
                default : {
                    buffer.append(c);
                    break;
                }
            }
        }
    }

    /**
     * @param list
     * @return String
     */
    public static String toXml(List<Table> list) {
        StringBuilder buffer = new StringBuilder();

        for(Iterator<Table> iterator = list.iterator(); iterator.hasNext();) {
            Table table = iterator.next();
            buffer.append(toXml(table));
        }
        return buffer.toString();
    }
}
